package graph;

import java.util.ArrayList;
import java.util.Collection;

/**
 * This class represents a simple Vertex object for a graph.
 * @author Alex Dusenbery
 * @version 1.0
 *
 */
@SuppressWarnings("rawtypes")
public class Vertex<T> implements Comparable{
	
	private T element;
	
	/**
	 * Returns the element inside this vertex.
	 * @return
	 */
	public T getElement() {
		return this.element;
	}
	
	/**
	 * Sets the element of this vertex.
	 * @param element
	 */
	public void setElement(T element) {
		this.element = element;
	}
	
	/**
	 * The name of the vertex.
	 */
	private String name;
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	/**
	 * A collection of edges going into the graph.
	 */
	private Collection<Edge> edges = new ArrayList<Edge>();
	
	/**
	 * Indicates whether this vertex has been visited in a search.
	 */
	private boolean visited;
	
	public void setVisited(boolean b) {
		visited = b;
	}
	
	public boolean isVisited() {
		return visited;
	}
	
	/**
	 * The path cost to this node in a search.
	 */
	private int pathCost;
	
	public int getPathCost() {
		return pathCost;
	}

	public void setPathCost(int pathCost) {
		this.pathCost = pathCost;
	}
	
	/**
	 * The parent of this node, for printing solution path.
	 */
	private Vertex solutionParent;
	

	public Vertex getSolutionParent() {
		return solutionParent;
	}

	public void setSolutionParent(Vertex solutionParent) {
		this.solutionParent = solutionParent;
	}
	
	private double heuristic;

	/**
	 * @return the heuristic
	 */
	public double getHeuristic() {
		return heuristic;
	}

	/**
	 * @param heuristic the heuristic to set
	 */
	public void setHeuristic(double heuristic) {
		this.heuristic = heuristic;
	}
	
	private double f_value;

	/**
	 * @return the f_value
	 */
	public double getF_value() {
		return f_value;
	}
	
	/**
	 * Sets the f value for this vertex.
	 * @param f
	 */
	public void setF_value(double f) {
		this.f_value = f;
	}

	/**
	 * Creates a new vertex with the specified name.  Since no element is provided, the element is set to
	 * the name by default.
	 * @param name
	 */
	@SuppressWarnings("unchecked")
	public Vertex(String name) {
		this.name = name;
		this.element = (T) name;
	}	
	
	public Vertex(T element, String name, double heuristic) {
		this.element = element;
		this.name = name;
		this.heuristic = heuristic;
	}
	
	/**
	 * Creates a new vertex with specified name and element inside it.
	 * @param element
	 * @param name
	 */
	public Vertex(T element, String name) {
		this.element = element;
		this.name = name;
	}
	
	/**
	 * Adds an edge incident to this vertex.
	 * @param e
	 * @return
	 * @throws Exception 
	 */
	public boolean putEdge(Edge e) throws Exception {
		if(!(e.getV1().equals(this) || e.getV2().equals(this)))
			throw new Exception("Neither end-vertex of this edge was equal to the vertex you're trying to attach it to!");
		if(!edges.contains(e)) {
			edges.add(e);
			return true;
		}
		else
			return false;
	}
	
	/**
	 * @return the edges
	 */
	public Collection<Edge> getEdges() {
		return edges;
	}

	/**
	 * Returns all the neighbors of this vertex.
	 * @return
	 */
	public Collection<Vertex> getNeighbors() {
		Collection<Vertex> neighbors = new ArrayList<Vertex>();
		for(Edge e: edges) {
			if(e.getV1().equals(this))
				neighbors.add(e.getV2());
			else if(!e.isDirected())
				neighbors.add(e.getV1());
				
		}
		return neighbors;
	}
	
	public int getDegree() {
		return this.edges.size();
	}
	
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("[");
		sb.append(name).append(",");		
		if(element != null)
			sb.append("element:").append(element.toString());
		
		sb.append("]");
		return sb.toString();
	}
	

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((element == null) ? 0 : element.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof Vertex)) {
			return false;
		}
		Vertex other = (Vertex) obj;
		if (element == null) {
			if (other.element != null) {
				return false;
			}
		} else if (!element.equals(other.element)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}

	@SuppressWarnings("unchecked")
	@Override
	public int compareTo(Object that) {
		return ((Comparable) this.name).compareTo(((Vertex)that).name);
	}

}
